#include "console.h"

#include <stdio.h>
#include <string.h>
#include "esp_system.h"
#include "esp_log.h"
#include "esp_console.h"
#include "esp_vfs_dev.h"
#include "driver/uart.h"
#include "linenoise/linenoise.h"
#include "argtable3/argtable3.h"
#include "esp_vfs_fat.h"
#include "nvs.h"
#include "nvs_flash.h"
#include "cmd_decl.h"

#define CONSOLE_UART UART_NUM_0
#define DETECT_TIMEOUT  1000
#define DETECT_CHAR_MAX 6

static const char *TAG = "console";

/* Console command history can be stored to and loaded from a file.
 * The easiest way to do this is to use FATFS filesystem on top of
 * wear_levelling library.
 */
#if CONFIG_CONSOLE_STORE_HISTORY

#define MOUNT_PATH "/data"
#define HISTORY_PATH MOUNT_PATH "/history.txt"

static void initialize_filesystem(void)
{
    static wl_handle_t wl_handle;
    const esp_vfs_fat_mount_config_t mount_config = {
            .max_files = 4,
            .format_if_mount_failed = true
    };
    esp_err_t err = esp_vfs_fat_spiflash_mount(MOUNT_PATH, "storage", &mount_config, &wl_handle);
    if (err != ESP_OK) {
        ESP_LOGE(TAG, "Failed to mount FATFS (%s)", esp_err_to_name(err));
        return;
    }
}
#endif // CONFIG_STORE_HISTORY

static void initialize_nvs(void)
{
    esp_err_t err = nvs_flash_init();
    if (err == ESP_ERR_NVS_NO_FREE_PAGES || err == ESP_ERR_NVS_NEW_VERSION_FOUND) {
        ESP_ERROR_CHECK( nvs_flash_erase() );
        err = nvs_flash_init();
    }
    ESP_ERROR_CHECK(err);
}

static void enter_detect(void)
{
    char buf[DETECT_CHAR_MAX+1]="";
    int len = 0;

    //安装驱动
    QueueHandle_t queue;
    ESP_ERROR_CHECK(uart_driver_install(CONSOLE_UART, UART_FIFO_LEN*2, 0, 6, &queue, 0));

    //刷新
    uart_flush(CONSOLE_UART);

    //处理串口输入
    while(1)
    {
        //逐字符接收
        int n = uart_read_bytes(CONSOLE_UART, buf+len, 1, pdMS_TO_TICKS(DETECT_TIMEOUT));
        if(n==0)
        {
            //超时未输入，直接清buffer
            len = 0;
            memset(buf,0,sizeof(buf));
        }
        else if(n>0)
        {
            len += n;

            //边界处理
            if(len >= DETECT_CHAR_MAX)
            {
                char* p = strchr(buf,'\r');
                if(p!=NULL)
                {
                    len = buf+DETECT_CHAR_MAX-p;
                    memcpy(buf,p,len);
                    memset(buf+len,0,sizeof(buf)-len);
                }
                else
                {
                    len = 0;
                    memset(buf,0,sizeof(buf));
                }
            }

            //判断
            if(strstr(buf,"\r\n\r\n\r\n")||strstr(buf,"\r\r\r")||strstr(buf,"\n\n\n"))
            {
                ESP_LOGI("Console", "Detected 3 lines!");
                break;
            }
        }
        else
        {
            ESP_LOGE("CONSOLE","Unknown Error(%d)!",n);
        }
    }

    uart_driver_delete(CONSOLE_UART);
}

static void console_detect_entry(void * param)
{
    //1s内检测到3个回车符后进入控制台模式
    enter_detect();

    //降低LOG等级
    esp_log_level_set("NTRIP",ESP_LOG_ERROR);
    esp_log_level_set("FHPL_ESP32_DEMO",ESP_LOG_ERROR);
    esp_log_level_set("MQTT",ESP_LOG_ERROR);

    //创建控制台任务
    esp_console_repl_t *repl = NULL;
    esp_console_repl_config_t repl_config = ESP_CONSOLE_REPL_CONFIG_DEFAULT();
    esp_console_dev_uart_config_t uart_config = ESP_CONSOLE_DEV_UART_CONFIG_DEFAULT();
    /* Prompt to be printed before each line.
     * This can be customized, made dynamic, etc.
     */
    repl_config.prompt = ">";
    repl_config.max_cmdline_length = CONFIG_CONSOLE_MAX_COMMAND_LINE_LENGTH;
#if CONFIG_CONSOLE_STORE_HISTORY
    repl_config.history_save_path = HISTORY_PATH;
#endif
    /* Register commands */
    esp_console_register_help_command();
    register_system();
    register_wifi();
    register_nvs();

    ESP_ERROR_CHECK(esp_console_new_repl_uart(&uart_config, &repl_config, &repl));
    ESP_ERROR_CHECK(esp_console_start_repl(repl));

    //删除当前任务
    vTaskDelete(NULL);
}

esp_err_t console_init(void)
{
    //初始化NVS
    initialize_nvs();

#if CONFIG_CONSOLE_STORE_HISTORY
    //初始化文件系统
    initialize_filesystem();
    ESP_LOGI(TAG, "Command history enabled");
#else
    ESP_LOGI(TAG, "Command history disabled");
#endif

    //创建控制台检测任务
    if(xTaskCreate(console_detect_entry, "console_detect",
            2048, NULL, 2, NULL)==pdTRUE)
    {
        return ESP_OK;
    }

    return ESP_FAIL;
}
